﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;
using CustomTypes.Interfaces;
using devtm.AutoMapper;
using System.Collections;
using Microsoft.RestrictedUsage.CSharp.Semantics;
using CustomTypes.Mono.CSharpParser;
using System.Diagnostics;

namespace CustomTypes.Mono
{

    [DebuggerDisplay("{AssemblyName}")]
    public class AssemblyTypeCSharpDescriptor : IAssemblyDescriptor
    {

        private DateTime lastWrite;
        private AssemblyDefinition assembly;
        private Microsoft.RestrictedUsage.CSharp.Semantics.ProjectAssembly projectAssembly;


        public AssemblyTypeCSharpDescriptor(TypeRepository typeRepository, Microsoft.RestrictedUsage.CSharp.Semantics.ProjectAssembly projectAssembly)
        {
            this.Repository = typeRepository;
            lastWrite = DateTime.MinValue;
            this.projectAssembly = projectAssembly;
        }

        #region assembly

        private AssemblyDefinition GetAssembly()
        {

            //if (lastWrite < System.IO.File.GetLastWriteTime(_filename))
            //    assembly = null;

            return assembly ?? (assembly = CreateAssembly());

        }

        private AssemblyDefinition CreateAssembly()
        {
            //lastWrite = System.IO.File.GetLastWriteTime(_filename);
            var ass = AssemblyDefinition.CreateAssembly(new AssemblyNameDefinition("", new Version("1.0.0.0")), "<module>", ModuleKind.Dll);
            
            var Types = ass.MainModule.Types;

            //foreach (CSharpType item in projectAssembly.Types)            
            //    CsTypeParser.Parse(item, Repository, ass.MainModule);                          

            return ass;
        }

        #endregion


        #region Implement ITypeContext

        public string AssemblyName { get { return GetAssembly().FullName; } }

        public string ShortAssemblyName { get { return GetAssembly().Name.Name; } }

        public TypeDefinition GetType(string @namespace, string name)
        {

            AssemblyDefinition ass = GetAssembly();

            var types = ass.MainModule.GetTypes();

            foreach (TypeDefinition type in types)
                if (type.Name == name)
                    if (type.Namespace == @namespace)
                        return new TypeDefinitionRuntime(type, type);

            return null;

        }


        public IEnumerable<string> Namespaces(Predicate<TypeDefinition> predicate)
        {

            HashSet<string> l = new HashSet<string>();

            AssemblyDefinition ass = GetAssembly();
            var types = ass.MainModule.GetTypes();

            foreach (TypeDefinition type in types)
                if (type.Name != "<Module>")
                {
                    if (predicate == null || predicate(type))
                    {
                        string n = type.Namespace;
                        if (!l.Contains(n))
                        {
                            l.Add(n);
                            yield return n;
                        }
                    }

                }

            l.Clear();


        }

        public IEnumerable<TypeDefinition> GetTypes(HashSet<string> namespaces)
        {

            AssemblyDefinition ass = GetAssembly();

            var types = ass.MainModule.GetTypes();

            foreach (TypeDefinition type in types)
                if (type.Name != "<Module>")
                    if (namespaces == null || @namespaces.Contains(type.Namespace))
                        yield return new TypeDefinitionRuntime(type, type);

        }

        public IEnumerable<TypeDefinition> GetTypes(Predicate<TypeDefinition> predicate, HashSet<string> namespaces = null)
        {
            AssemblyDefinition ass = GetAssembly();

            var types = ass.MainModule.GetTypes();

            foreach (TypeDefinition type in types)
                if (type.Name != "<Module>")
                    if (predicate == null || predicate(type))
                        if (namespaces == null || @namespaces.Contains(type.Namespace))
                            yield return new TypeDefinitionRuntime(type, type);

        }

        public void Refresh() { assembly = null; }
        public void Clear() { assembly = null; }

        #endregion


        public TypeRepository Repository { get; set; }

    }
}
